package org.apache.httpclient;

import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

public class MultiConnPool
{
    
    private PoolingClientConnectionManager cm;
    private DefaultHttpClient httpClient;
    
    public DefaultHttpClient getHttpClient()
    {
        return httpClient;
    }


    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            10, 20, 5, TimeUnit.MINUTES, new LinkedTransferQueue<Runnable>(),
            new ThreadFactory()
            {
                int i = 0;

                @Override
                public Thread newThread(Runnable r)
                {
                    Thread t = new Thread(r);
                    t.setName("MultiConnPool-" + i++);
                    t.setDaemon(true);
                    return t;
                }
            });
    
    public MultiConnPool(){
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        cm = new PoolingClientConnectionManager(registry);
        cm.setDefaultMaxPerRoute(5);
        cm.setMaxTotal(20);
        httpClient = new DefaultHttpClient(cm);
        httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler());
        httpClient.setReuseStrategy(new DefaultConnectionReuseStrategy());
        httpClient.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        httpClient.addResponseInterceptor(null);
        Thread t = new IdleConnectionMonitorThread(cm);
        t.setName("idle-connection-monitor");
        t.setDaemon(true);
        t.start();
    }
    
    
    
    private ManagedClientConnection getConnection(String host,long time,TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException{
        HttpRoute route = new HttpRoute(new HttpHost(host));
        ClientConnectionRequest clientConnectionRequest = cm.requestConnection(route, EXECUTOR);
        ManagedClientConnection connection = clientConnectionRequest.getConnection(time, unit);
        connection.setIdleDuration(30, TimeUnit.SECONDS);
        return connection;
    }
    
    public String conn() throws IOException 
    {
        ManagedClientConnection conn = null;
        try
        {
            conn = getConnection("www.baidu.com", 5, TimeUnit.SECONDS);
            BasicHttpEntityEnclosingRequest requestEntity = new BasicHttpEntityEnclosingRequest("GET", "/");
            conn.sendRequestHeader(requestEntity);
            conn.sendRequestEntity(null);
            conn.flush();
            
            HttpResponse response = conn.receiveResponseHeader();

            if(true){
                System.out.println(response.getStatusLine());
                System.out.println(Arrays.toString(response.getAllHeaders()));
            }
            
            conn.receiveResponseEntity(response);
            HttpEntity entity = response.getEntity();
            if (entity != null)
            {
                BasicManagedEntity managedEntity = new BasicManagedEntity(entity, conn, true);
                // Replace entity
                response.setEntity(managedEntity);
            }
            // Do something useful with the response
            // The connection will be released automatically
            // as soon as the response content has been consumed
            
        }
        catch (IOException e)
        {
            // Abort connection upon an I/O error.
                conn.abortConnection();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (HttpException e)
        {
            conn.abortConnection();
            e.printStackTrace();
        }
        return null;
    }
    
    public String con(String uri){
        HttpClient client = getHttpClient();
        HttpGet get = new HttpGet(uri);
        get.addHeader(new BasicHeader("Connection",HTTP.CONN_KEEP_ALIVE));
//        get.addHeader("Accept-Encoding", "gzip,deflate,sdch");
        String result = null;
        try
        {
            HttpResponse response =  client.execute(get);
            StatusLine status = response.getStatusLine();
            System.out.println("---------------------------");
            System.out.println(status.toString());
            System.out.println("---------------------------");
            HttpEntity entity = response.getEntity();
//            HttpEntity entity =new GzipDecompressingEntity(response.getEntity());
            result =  EntityUtils.toString(entity);
            EntityUtils.consume(entity);
        }
        catch (ClientProtocolException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }finally{
//            client.getConnectionManager().shutdown();
//            client.getConnectionManager().releaseConnection(null, 0, null);
        }
        
        return result;
    }
    
    private class Task implements Callable<String>{
        
        private String uri;
        private Task(String uri){
            this.uri = uri;
        }

        @Override
        public String call() throws Exception
        {
            return con(uri);
        }
    }
    
    
    public Future<String> asynGetHttpResponse(String uri){
        return EXECUTOR.submit(new Task(uri));
    }
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException
    {
        MultiConnPool pool =   new MultiConnPool();
        
        Future<String> value = pool.asynGetHttpResponse("http://www.baidu.com");
        
        System.out.println("execute iteye");
//        String str = pool.con("http://www.baidu.com");
//        System.out.println(str);
        long time = System.currentTimeMillis();
        System.out.println(pool.cm.getTotalStats());
        System.out.println(value.get());
        long time2 = System.currentTimeMillis();
        Future<String> value2 = pool.asynGetHttpResponse("http://www.iteye.com/blogs?page=6");
        System.out.println(pool.cm.getTotalStats());
        System.out.println(value2.get());
        long time3 = System.currentTimeMillis();
        Future<String> value3 = pool.asynGetHttpResponse("http://www.iteye.com/blogs?page=9");
        System.out.println(pool.cm.getTotalStats());
        System.out.println(time2- time);
        System.out.println(time3-time2);
        System.out.println(pool.cm.getTotalStats());
        
        Thread.sleep(100000);
        System.out.println(pool.cm.getTotalStats());
        Future<String> value4 = pool.asynGetHttpResponse("http://www.iteye.com/blogs?page=9");
        
        value4.get();
//        656
//        3666
//        2200
//        1995
//        479
//        404        
    }
    
    
    public static class IdleConnectionMonitorThread extends Thread
    {
        private final ClientConnectionManager connMgr;
        private volatile boolean shutdown;

        public IdleConnectionMonitorThread(ClientConnectionManager connMgr)
        {
            super();
            this.connMgr = connMgr;
        }

        @Override
        public void run()
        {
            try
            {
                while (!shutdown)
                {
                    synchronized (this)
                    {
                        wait(5000);
                        // Close expired connections
                        connMgr.closeExpiredConnections();
                        // Optionally, close connections
                        // that have been idle longer than 30 sec
                        connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
                    }
                }
            }
            catch (InterruptedException ex)
            {
                // terminate
            }
        }

        public void shutdown()
        {
            shutdown = true;
            synchronized (this)
            {
                notifyAll();
            }
        }
    }
}
